package com.tianji.learning.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CategoryClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.client.learning.LearningClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.query.PageQuery;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.dto.LearningPlanDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.domain.vo.LearningLessonVO;
import com.tianji.learning.domain.vo.LearningPlanPageVO;
import com.tianji.learning.domain.vo.LearningPlanVO;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.PlanStatus;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 学生课程表 服务实现类
 * </p>
 *
 * @author zzj
 * @since 2024-04-18
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class LearningLessonServiceImpl extends ServiceImpl<LearningLessonMapper, LearningLesson> implements ILearningLessonService {

    final CourseClient courseClient;
    final CatalogueClient catalogueClient;
    final LearningClient learningClient;
    final LearningRecordMapper recordMapper;

    @Override
    public void addUserLesson(Long userId, List<Long> courseIds) {
        //通过feign远程调用课程服务
        List<CourseSimpleInfoDTO> cinfos = courseClient.getSimpleInfoList(courseIds);

        //封装po实体类，填充过期时间
        List<LearningLesson> list = new ArrayList<>();
        for (CourseSimpleInfoDTO cinfo : cinfos) {
            LearningLesson lesson = new LearningLesson();
            lesson.setUserId(userId);
            lesson.setCourseId(cinfo.getId());
            Integer validDuration = cinfo.getValidDuration();//课程有效期 单位是月
            if (validDuration != null) {
                LocalDateTime now = LocalDateTime.now();
                lesson.setCreateTime(now);
                lesson.setExpireTime(now.plusMonths(validDuration));
            }
            list.add(lesson);
        }

        //批量保存
        this.saveBatch(list);

    }

    @Override
    public PageDTO<LearningLessonVO> queryMyLessons(PageQuery query) {
        //获取当前用户id
        Long userId = UserContext.getUser();
        if (userId == null) {
            throw new BadRequestException("必须得登录");
        }

        //分页查询我的课表
        Page<LearningLesson> page = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .page(query.toMpPage("latest_learn_time", false));
        List<LearningLesson> records = page.getRecords();
        if (CollUtils.isEmpty(records)) {
            return PageDTO.empty(page);
        }

        //远程调用课程服务，给vo中的课程名  封面 章节数据赋值
        List<Long> courseIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toList());
        List<CourseSimpleInfoDTO> cinfos = courseClient.getSimpleInfoList(courseIds);
        if (cinfos == null) {
            throw new BizIllegalException("课程不存在");
        }
        //将cinfos 课程集合转化为 Map结构<课程id, 课程对象>
        Map<Long, CourseSimpleInfoDTO> infoDTOMap = cinfos.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, c -> c));

        //将po中的数据，封装到vo中
        List<LearningLessonVO> voList = new ArrayList<>();
        for (LearningLesson record : records) {
            LearningLessonVO vo = BeanUtils.copyBean(record, LearningLessonVO.class);
            CourseSimpleInfoDTO infoDTO = infoDTOMap.get(record.getCourseId());
            if (infoDTO != null) {
                vo.setCourseName(infoDTO.getName());
                vo.setCourseCoverUrl(infoDTO.getCoverUrl());
                vo.setSections(infoDTO.getSectionNum());
            }
            voList.add(vo);
        }
        //返回
        return PageDTO.of(page, voList);
    }

    @Override
    public LearningLessonVO queryMyCurrentLesson() {
        //获取当前登录用户id
        Long userId = UserContext.getUser();

        //查询当前用户最近学习课程 按latest_learn_time 降序排序 取第一条 正在学习中的 status=1
        //sql: select * from learning_lesson where user_id = xxx and status = 1 order by latest_learn_time desc limit 1
        LearningLesson lesson = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getStatus, LessonStatus.LEARNING)
                .orderByDesc(LearningLesson::getLatestLearnTime)
                .last("limit 1")
                .one();
        if (lesson == null) {
            return null;
        }

        //远程调用课程服务，给vo中的课程名称 封面 章节数据赋值
        CourseFullInfoDTO cinfo = courseClient.getCourseInfoById(lesson.getId(), false, false);
        if (cinfo == null) {
            throw new BizIllegalException("课程不存在");
        }

        //查询当前用户课表中 已报名 总的课程数
        //select count(*) from learning_lesson where user_id = xxx
        Integer count = this.lambdaQuery().eq(LearningLesson::getUserId, userId).count();

        //通过feign远程调用 课程服务 获取小节名称 和小节编号
        Long latestSectionId = lesson.getLatestSectionId();//最近学习的小节id
        List<CataSimpleInfoDTO> cataSimpleInfoDTOS = catalogueClient.batchQueryCatalogue(CollUtils.singletonList(latestSectionId));
        if (CollUtils.isEmpty(cataSimpleInfoDTOS)) {
            throw new BizIllegalException("小节不存在");
        }

        //封装vo
        LearningLessonVO vo = BeanUtils.copyBean(lesson, LearningLessonVO.class);
        vo.setCourseName(cinfo.getName());
        vo.setCourseCoverUrl(cinfo.getCoverUrl());
        vo.setSections(cinfo.getSectionNum());
        vo.setCourseAmount(count);//当前用户能学习课程总数
        CataSimpleInfoDTO cataSimpleInfoDTO = cataSimpleInfoDTOS.get(0);
        vo.setLatestSectionName(cataSimpleInfoDTO.getName());//最近学习的小节名称
        vo.setLatestSectionIndex(cataSimpleInfoDTO.getCIndex());//最近学习的小节序号

        return vo;
    }

    @Override
    public Long isLessonValid(Long courseId) {
        //获取当前登录用户id
        Long userId = UserContext.getUser();

        //查询当前用户课表 状态没有失效 status = 3
        LearningLesson lesson = this.lambdaQuery()
                .eq(LearningLesson::getCourseId, courseId)
                .eq(LearningLesson::getUserId, userId)
                .ne(LearningLesson::getStatus, LessonStatus.EXPIRED)
                .one();
        if (lesson == null) {
            return null;
        }

        //判断是否过期
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime expireTime = lesson.getExpireTime();
        if (expireTime != null || now.isAfter(expireTime)) {
            //已过期
            return null;
        }

        return lesson.getId();
    }

    @Override
    public LearningLessonVO queryLessonStatus(Long courseId) {
        //获取当前登录用户id
        Long userId = UserContext.getUser();

        //根据课程id，查询当前用户的课表中是否有该课程，
        LearningLesson lesson = this.lambdaQuery()
                .eq(LearningLesson::getCourseId, courseId)
                .eq(LearningLesson::getUserId, userId)
                .one();
        if (lesson == null) {
            return null;
        }

        // 如果有该课程则需要返回课程的学习进度、课程有效期等信息。
        LearningLessonVO vo = BeanUtils.copyBean(lesson, LearningLessonVO.class);

        return vo;
    }

    @Override
    public Integer countLearningLessonByCourse(Long courseId) {
        //根据课程id，统计表中该课程的已报名数量
        Integer count = this.lambdaQuery()
                .eq(LearningLesson::getCourseId, courseId)
                .count();

        return count;
    }

    @Override
    public void createLearningPlan(LearningPlanDTO dto) {
        //获取当前登录用户id
        Long userId = UserContext.getUser();

        //根据课程id，查询当前用户的课表中是否有该课程，
        LearningLesson lesson = this.lambdaQuery()
                .eq(LearningLesson::getCourseId, dto.getCourseId())
                .eq(LearningLesson::getUserId, userId)
                .one();
        if (lesson == null) {
            throw new BizIllegalException("该课程没有加入课表");
        }
        //修改课表    两种都可以  全部更新
//        lesson.setWeekFreq(dto.getFreq());
//        lesson.setPlanStatus(PlanStatus.PLAN_RUNNING);
//        this.updateById(lesson);
        //链式编程 只更新一个字段
        this.lambdaUpdate()
                .set(LearningLesson::getWeekFreq,dto.getFreq())
                .set(LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .eq(LearningLesson::getId,lesson.getId())
                .update();
    }

    @Override
    public LearningPlanPageVO queryMyPlans(PageQuery query) {
        //获取当前登录用户id
        Long userId = UserContext.getUser();

        //todo：查询积分

        //查询本周学习计划总数据 learning_lesson 条件 userId status in(0,1) plan_status=1 ,查询sum(week_freq)
        //SELECT SUM(week_freq) FROM `learning_lesson`
        //WHERE user_id = 2
        //AND plan_status = 1
        //AND `status` in(0,1)
        QueryWrapper<LearningLesson> wrapper = new QueryWrapper<>();
        wrapper.select("sum(week_freq) as plansTotal");//查询哪些列
        wrapper.eq("user_id",userId);
        wrapper.in("status",LessonStatus.NOT_BEGIN,LessonStatus.LEARNING);
        wrapper.eq("plan_status",PlanStatus.PLAN_RUNNING);
        Map<String, Object> map = this.getMap(wrapper);//map有可能为空，下面的toString可能会报空指针异常
        //{plansTotal：7}
        Integer plansTotal = 0;
        if (map != null && map.get("plansTotal") != null){
            plansTotal = Integer.valueOf(map.get("plansTotal").toString());
        }

        //查询本周实际已学习的小节总数据 learning_record 条件 userId finish_time在本周区间之内 finished为true count(*)
        //SELECT COUNT(*) FROM learning_record
        //WHERE user_id = 2
        //AND finished = 1
        //AND finish_time BETWEEN '2022-10-31 00:30:00' AND '2022-10-31 00:31:00'
        LocalDate now = LocalDate.now();
        LocalDateTime weekBeginTime = DateUtils.getWeekBeginTime(now);//本周开始的时间
        LocalDateTime weekEndTime = DateUtils.getWeekEndTime(now);//本周结束时间
        Integer weekFinishedPlanNum = recordMapper.selectCount(Wrappers.<LearningRecord>lambdaQuery()
                .eq(LearningRecord::getUserId, userId)
                .eq(LearningRecord::getFinished, true)
                .between(LearningRecord::getFinishTime, weekBeginTime, weekEndTime));

        //查询课表数据 learning_lessons 条件userId status in(0,1) plan_status=1
        Page<LearningLesson> page = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .in(LearningLesson::getStatus, LessonStatus.NOT_BEGIN, LessonStatus.LEARNING)
                .eq(LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .page(query.toMpPage("latest_learn_time", false));
        List<LearningLesson> records = page.getRecords();
        if (CollUtils.isEmpty(records)){
            LearningPlanPageVO vo = new LearningPlanPageVO();
            vo.setPages(0L);
            vo.setTotal(0L);
            vo.setList(CollUtils.emptyList());
            return vo;
        }

        //远程调用课程服务 获取课程信息
        Set<Long> courseIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet());
        List<CourseSimpleInfoDTO> cinfos = courseClient.getSimpleInfoList(courseIds);
        if (CollUtils.isEmpty(cinfos)){
            throw new BizIllegalException("课程不存在");
        }
        //将cinfos List结构转Map<课程id，CourseSimpleInfoDTO>
        Map<Long, CourseSimpleInfoDTO> cInfoMap = cinfos.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, c -> c));

        //查询学习记录表learning_record 本周 当前用户下 每一门课下 已学习的小节数量
        //-- 查询学习记录表learning_record 本周 当前用户下 每一门课下 已学习的小节数量
        //SELECT lesson_id,count(*) FROM `learning_record`
        //WHERE user_id = 2
        //AND finished = 1
        //AND finish_time BETWEEN '2022-10-20 16:29:21' AND '2022-10-20 16:31:21'
        //GROUP BY lesson_id
        QueryWrapper<LearningRecord> rWrapper = new QueryWrapper<>();
        rWrapper.select("lesson_id as lessonId", "count(*) as userId");
        rWrapper.eq("user_id",userId);
        rWrapper.eq("finished",true);
        rWrapper.between("finish_time",weekBeginTime,weekEndTime);
        rWrapper.groupBy("lesson_id");
        List<LearningRecord> learningRecords = recordMapper.selectList(rWrapper);
        //LearningRecord(id=null, lessonId=1, sectionId=null, userId=1, moment=null, finished=null, createTime=null, finishTime=null, updateTime=null)
        //map中的key的lessonId  value是当前用户对该课程下已学习的小节数量
        Map<Long, Long> courseWeekFinishNumMap = learningRecords.stream().collect(Collectors.toMap(LearningRecord::getLessonId, c -> c.getUserId()));


        //封装vo
        LearningPlanPageVO vo = new LearningPlanPageVO();
        vo.setWeekTotalPlan(plansTotal);
        vo.setWeekFinished(weekFinishedPlanNum);
        List<LearningPlanVO> voList = new ArrayList<>();
        for (LearningLesson record : records) {
            LearningPlanVO planVO = BeanUtils.copyBean(record, LearningPlanVO.class);
            CourseSimpleInfoDTO infoDTO = cInfoMap.get(record.getCourseId());
            if (infoDTO != null){
                planVO.setCourseName(infoDTO.getName());//课程名
                planVO.setSections(infoDTO.getSectionNum());//课程下的总小节数
            }
//            Long aLong = courseWeekFinishNumMap.get(record.getId());
//            if (aLong != null){
//                planVO.setWeekLearnedSections(aLong.intValue());//本周已学习的章节数
//            }else {
//                planVO.setWeekLearnedSections(0);
//            }
            planVO.setWeekLearnedSections(courseWeekFinishNumMap.getOrDefault(record.getId(),0L).intValue());

            voList.add(planVO);
        }
//        vo.setList(voList);
//        vo.setTotal(page.getTotal());
//        vo.setPages(page.getPages());
        return vo.pageInfo(page.getTotal(),page.getPages(),voList);
    }
}
